package com.kapcb.assemble.strategy;

import com.google.common.collect.Maps;
import com.kapcb.assemble.annotation.Strategy;
import org.apache.commons.collections4.MapUtils;
import org.springframework.aop.support.AopUtils;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.SmartInitializingSingleton;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.util.ClassUtils;

import java.util.Map;
import java.util.Objects;

/**
 * <a>Title: DefaultStrategySelector </a>
 * <a>Author: Kapcb <a>
 * <a>Description: DefaultStrategySelector <a>
 *
 * @author Kapcb
 * @version 1.0
 * @date 2024/4/17 22:43
 * @since 1.0
 */
public class DefaultStrategySelector extends AbstractStrategySelector implements ApplicationContextAware, SmartInitializingSingleton {

    private ApplicationContext applicationContext;

    private Map<String, Object> strategyBeanMap = Maps.newConcurrentMap();

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }

    @Override
    protected <T> T selectStrategy(String bizCode) {
        Object service = strategyBeanMap.get(bizCode);
        if (Objects.isNull(service)) {
            throw new RuntimeException("no strategy found by biz code : " + bizCode);
        }
        return (T) service;
    }

    @Override
    public void afterSingletonsInstantiated() {
        // 在 IOC 容器中找到所有被 Strategy 修饰的 Bean
        Map<String, Object> refreshStrategyBeanMap = applicationContext.getBeansWithAnnotation(Strategy.class);
        if (MapUtils.isEmpty(refreshStrategyBeanMap)) {
            return;
        }

        refreshStrategyBeanMap.forEach((key, value) -> {
            Class<?> strategyClass = value.getClass();
            if (AopUtils.isAopProxy(value)) {
                strategyClass = ClassUtils.getUserClass(value);
            }

            Strategy strategy = AnnotationUtils.findAnnotation(strategyClass, Strategy.class);
            String strategyBeanKey = StrategyCodeBuilder.buildKey(strategy.bizCode(), strategy.type());
            Object existBean = strategyBeanMap.put(strategyBeanKey, value);

            // 重复注册检查
            if (Objects.nonNull(existBean)) {
                throw new RuntimeException("biz : " + strategyBeanKey + " already register, please check your strategy bean!");
            }
        });
    }
}
